﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using Mesuro.Common;
using Mesuro.Graphing.Utilities;
using Vector = Mesuro.Common.Vector;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;

namespace Mesuro.Graphing {
	[AxisProperty( "DisplayUnit", Label = "Display Unit", Converter = typeof( AngularUnitConverter ) )]
	[PlotProperty( "AxialUnit", Label = "Angular Unit", Converter = typeof( AngularUnitConverter ) )]
	public class PolarAxes : MultiAxes {
		public const string Key = "Polar Chart";
		private const double SQRT2 = 1.41421356237;

		#region DP: AxialUnit
		public static readonly DependencyProperty AxialUnitProperty = DependencyProperty.RegisterAttached( "AxialUnit", typeof( AngularUnit ), typeof( PolarAxes ), new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.None, AxialUnitChangedStatic ) );
		public AngularUnit AxialUnit {
			get { return (AngularUnit)GetValue( AxialUnitProperty ); }
			set { SetValue( AxialUnitProperty, value ); }
		}
		public static AngularUnit GetAxialUnit( DependencyObject element ) { return (AngularUnit)element.GetValue( AxialUnitProperty ); }
		public static void SetAxialUnit( DependencyObject element, AngularUnit value ) { element.SetValue( AxialUnitProperty, value ); }
		private static void AxialUnitChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) {
			PolarAxes axes = sender as PolarAxes;
			if (axes != null)
				axes.OnAxialUnitChanged( (AngularUnit)e.OldValue, (AngularUnit)e.NewValue );
		}
		#endregion

		#region DP: DisplayUnit
		public static readonly DependencyProperty DisplayUnitProperty = DependencyProperty.RegisterAttached( "DisplayUnit", typeof( AngularUnit ), typeof( PolarAxes ), new FrameworkPropertyMetadata( AngularUnit.Binary, FrameworkPropertyMetadataOptions.None, DisplayUnitChangedStatic ) );
		public AngularUnit DisplayUnit {
			get { return (AngularUnit)GetValue( DisplayUnitProperty ); }
			set { SetValue( DisplayUnitProperty, value ); }
		}
		public static AngularUnit GetDisplayUnit( DependencyObject element ) { return (AngularUnit)element.GetValue( DisplayUnitProperty ); }
		public static void SetDisplayUnit( DependencyObject element, AngularUnit value ) { element.SetValue( DisplayUnitProperty, value ); }
		private static void DisplayUnitChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) {
			PolarAxes axes = sender as PolarAxes;
			if (axes != null)
				axes.OnDisplayUnitChanged( (AngularUnit)e.OldValue, (AngularUnit)e.NewValue );
		}
		#endregion

		#region DP: Whiteout
		public static readonly DependencyProperty WhiteoutProperty = DependencyProperty.RegisterAttached( "Whiteout", typeof( Brush ), typeof( PolarAxes ), new FrameworkPropertyMetadata( new SolidColorBrush( Color.FromArgb( 0x99, 0xFF, 0xFF, 0xFF ) ), FrameworkPropertyMetadataOptions.AffectsRender, WhiteoutChangedStatic ) );
		public Brush Whiteout {
			get { return (Brush)GetValue( WhiteoutProperty ); }
			set { SetValue( WhiteoutProperty, value ); }
		}
		public static Brush GetWhiteout( DependencyObject element ) { return (Brush)element.GetValue( WhiteoutProperty ); }
		public static void SetWhiteout( DependencyObject element, Brush value ) { element.SetValue( WhiteoutProperty, value ); }
		private static void WhiteoutChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
		#endregion

		public double DataRadius {
			get { return scaleRadius.Range.Max; }
			set { scaleRadius.Range = new Limits( 0, value ); }
		}

		private VisibleScale scaleRadius = new VisibleScale();
		private VisibleScale scaleAngle = new VisibleScale() { Flags = VisibleScaleFlags.LockRange, Range = new Limits( 0, 2 ) };

		public new EllipseGeometry PlotArea {
			get { return (EllipseGeometry)base.PlotArea; }
			set { base.PlotArea = value; }
		}

		private Rectangle _selection = new Rectangle();
		protected Visual SelectionShape {
			get { return _selection; }
		}
		public Rectangle Selection {
			get {
				return _selection;
			}
		}

		static PolarAxes() {
			AxialUnitProperty.OverrideMetadata( typeof( PolarAxes ), new FrameworkPropertyMetadata( AngularUnit.Radian ) );
			SnapsToDevicePixelsProperty.OverrideMetadata( typeof( PolarAxes ), new FrameworkPropertyMetadata( true ) );
		}
		public PolarAxes()
			: base( 2 ) {
			PlotArea = new EllipseGeometry();
			scaleAngle.PropertyChanged += OnAxialScalePropertyChanged;
		}

		protected override void OnAddPlot( Plot plot ) {
			base.OnAddPlot( plot );
			SetGraphTransform( plot );
		}

		protected void SetGraphTransform( Plot plot ) {
			plot.AxesTransform = new PolarTransformedSeries( GetAxialUnit( plot ) ?? AxialUnit );
		}

		protected override Size ArrangeOverride( Size graphSize ) {

			Point c = PlotArea.Center = new Point( graphSize.Width / 2, graphSize.Height / 2 );
			double rad = PlotArea.RadiusX = PlotArea.RadiusY =
				ShowLabels ? Math.Min( graphSize.Width - 2 * textClearance - 2 * TextPadding, graphSize.Height - 2 * (ScaleTextSize * SQRT2 + TextPadding) ) / 2
				: Math.Min( graphSize.Width, graphSize.Height ) / 2;

			scaleRadius.NumTicks = (int)(2 * rad / MinimumTickSpacing + 0.5);
			scaleRadius.Domain = new Limits( 0, rad );

			scaleAngle.NumTicks = (DisplayUnit == AngularUnit.Degree || DisplayUnit == AngularUnit.Clock) ? 12 : (int)(2 * Math.PI * rad / MinimumTickSpacing + 0.5);
			scaleAngle.Domain = new Limits( 0, 2 * Math.PI * rad );

			foreach (Plot plot in Plots) {
				Limits xl = plot.PlotData.Channels[0].Limits, yl = plot.PlotData.Channels[1].Limits;
				double lt, rt, tp, bm;
				lt = c.X + scaleRadius.ToPlotSpace( xl.Min );
				tp = c.Y - scaleRadius.ToPlotSpace( yl.Max );
				rt = c.X + scaleRadius.ToPlotSpace( xl.Max );
				bm = c.Y - scaleRadius.ToPlotSpace( yl.Min );

				plot.Arrange( new Rect( lt, tp, Math.Abs( rt - lt ), Math.Abs( bm - tp ) ) );
			}

			return graphSize;
		}

		protected virtual void OnAxialScalePropertyChanged( object sender, PropertyChangedEventArgs e ) {
			switch (e.PropertyName) {
				case VisibleScale.PROP_Ticks:
					_measureTickText(); break;
				case VisibleScale.PROP_VisibleRange:
					InvalidateVisual();
					break;
				case VisibleScale.PROP_Invert:
					InvalidateArrange();
					break;
			}
		}

		protected virtual void OnDisplayUnitChanged( AngularUnit oldValue, AngularUnit newValue ) {
			if (newValue == AngularUnit.Degree || newValue == AngularUnit.Clock) {
				scaleAngle.Flags |= VisibleScaleFlags.LockNumTicks;
				scaleAngle.NumTicks = 12;
			} else {
				scaleAngle.Flags &= ~VisibleScaleFlags.LockNumTicks;
			}
			InvalidateArrange();
		}
		protected virtual void OnAxialUnitChanged( AngularUnit oldValue, AngularUnit newValue ) {
			_measureTickText();
			InvalidateArrange();
		}

		private double textClearance;
		private void _measureTickText() {
			textClearance = 0;

			for (int i = 0; i < scaleAngle.NumTicks; i++) {
				Size sz; double plot;
				decimal val = scaleAngle.GetTick( i, out plot );
				CreateGlyphRun( val.ToString(), ScaleTextSize, Typeface, out sz );
				textClearance = Math.Max( textClearance, sz.Width * SQRT2 );
			}

		}

		protected override void OnRender( DrawingContext cx ) {

			cx.DrawGeometry( PlotBackground, ScalePen, PlotArea );
			cx.PushTransform( new TranslateTransform( PlotArea.Center.X, PlotArea.Center.Y ) );
			{
				double plotRadius = PlotArea.RadiusX;
				Point centre = new Point( 0, 0 );
				int minRat = 5;
				int tNumTicks = scaleAngle.NumTicks * minRat;

				if (Graph.GetIsIcon( this )) {
					minRat = 1;
					tNumTicks = 8;
				}

				double value;
				AngularUnit dUnit = DisplayUnit;


				//Draw radial half-lines
				for (int i = 1; i < tNumTicks; i++) {
					double th = i * Math.PI * 2 / tNumTicks;
					double cos = Math.Cos( th ), sin = Math.Sin( th );
					Point end = new Point( centre.X + plotRadius * cos, centre.Y - plotRadius * sin );

					if (i % minRat == 0) {
						cx.DrawLine( MajorGridlines, centre, end );

						Size size;
						GlyphRun gr = CreateGlyphRun( (dUnit ?? AngularUnit.Radian).ToString( th, AngularUnit.Radian ), ScaleTextSize, Typeface, out size );

						if (ShowLabels) {
							// sender / SQRT2 == sender * SQRT2 / 2
							cx.PushTransform( new TranslateTransform(
								end.X + TextPadding * cos + size.Width / SQRT2 * (cos - 0.66666666),
								end.Y - TextPadding * sin - size.Height / SQRT2 * (sin - 0.66666666)
							) );
							cx.DrawGlyphRun( ScaleTextBrush, gr );
							cx.Pop();
						}
					} else
						cx.DrawLine( MinorGridlines, centre, end );
				}

				//Draw concentric contours
				Circ contour = new Circ();
				contour.Centre = centre;
				for (int i = 1; i <= scaleRadius.NumTicks * minRat; i++) {
					decimal labelValue = scaleRadius.GetTick( i / (double)minRat, out value );
					contour.Radius = value;
					if (i % minRat == 0) {
						cx.DrawEllipse( null, MajorGridlines, contour );

						Size size;
						GlyphRun gr = CreateGlyphRun( labelValue.ToString(), ScaleTextSize, Typeface, out size );

						if (ShowLabels) {
							cx.DrawRectangle( Whiteout, null, new Rect( centre.X + value - size.Width / 2 - TextPadding / 2, centre.Y + TextPadding / 2, size.Width + TextPadding, size.Height + TextPadding ) );
							cx.PushTransform( new TranslateTransform( value - size.Width / 2, size.Height + TextPadding ) );
							cx.DrawGlyphRun( ScaleTextBrush, gr );
							cx.Pop();
						}
					} else
						cx.DrawEllipse( null, MinorGridlines, contour );
				}

				//Draw 0 half-line
				cx.DrawLine( ScalePen, centre, new Point( centre.X + plotRadius, centre.Y ) );

			} cx.Pop();

		}

		protected Vector ToDataSpace( Point coord ) {
			double dx = coord.X - PlotArea.Center.X,
				   dy = coord.Y - PlotArea.Center.Y;
			return new Vector(
				DisplayUnit.ConvertFrom( AngularUnit.Radian, Math.Atan2( dy, dx ) ),//angle
				Math.Sqrt( dx * dx + dy * dy ) //radius
			);
		}
		protected Point ToPlotSpace( Vector datum ) {
			return new Point(
				PlotArea.Center.X + datum[0] * Math.Cos( AxialUnit.ConvertTo( AngularUnit.Radian, datum[1] ) ),
				PlotArea.Center.Y - datum[0] * Math.Sin( AxialUnit.ConvertTo( AngularUnit.Radian, datum[1] ) )
				);
		}

		protected override void OnRenderSizeChanged( SizeChangedInfo sizeInfo ) {
			base.OnRenderSizeChanged( sizeInfo );
			InvalidateArrange();
			InvalidateVisual();
		}
	}
}

//using System;
//using System.Windows;
//using System.Windows.Media;
//using Mesuro.Common;
//using Vector = Mesuro.Common.Vector;
//using System.Windows.Controls;
//using Mesuro.Graphing.Utilities;

//namespace Mesuro.Graphing {
//    public class PolarAxes : AbstractAxes {
//        public static readonly String Key = "Polar Plot";

//        #region DP: DefaultUnit
//        public static readonly DependencyProperty DefaultUnitProperty = DependencyProperty.RegisterAttached( "DefaultUnit", typeof( AngularUnit ), typeof( PolarAxes ), new FrameworkPropertyMetadata( AngularUnit.Radian, FrameworkPropertyMetadataOptions.None, DefaultUnitChangedStatic ) );
//        public AngularUnit DefaultUnit {
//            get { return (AngularUnit)GetValue( DefaultUnitProperty ); }
//            set { SetValue( DefaultUnitProperty, value ); }
//        }
//        public static AngularUnit GetDefaultUnit( DependencyObject element ) { return (AngularUnit)element.GetValue( DefaultUnitProperty ); }
//        public static void SetDefaultUnit( DependencyObject element, AngularUnit value ) { element.SetValue( DefaultUnitProperty, value ); }
//        private static void DefaultUnitChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
//        #endregion

//        #region DP: DisplayUnit
//        public static readonly DependencyProperty DisplayUnitProperty = DependencyProperty.RegisterAttached( "DisplayUnit", typeof( AngularUnit ), typeof( PolarAxes ), new FrameworkPropertyMetadata( AngularUnit.Degree, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender, DisplayUnitChangedStatic ) );
//        public AngularUnit DisplayUnit {
//            get { return (AngularUnit)GetValue( DisplayUnitProperty ); }
//            set { SetValue( DisplayUnitProperty, value ); }
//        }
//        public static AngularUnit GetDisplayUnit( DependencyObject element ) { return (AngularUnit)element.GetValue( DisplayUnitProperty ); }
//        public static void SetDisplayUnit( DependencyObject element, AngularUnit value ) { element.SetValue( DisplayUnitProperty, value ); }
//        private static void DisplayUnitChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) {
//            UIElement ui = sender as UIElement;
//            if (ui != null)
//                ui.RaiseEvent( new DPropChangedRoutedEventArgs<AngularUnit>( DisplayUnitChangedEvent, (AngularUnit)e.OldValue, (AngularUnit)e.NewValue ) );
//        }

//        public static readonly RoutedEvent DisplayUnitChangedEvent = EventManager.RegisterRoutedEvent( "DisplayUnitChanged", RoutingStrategy.Direct, typeof( DPropChangedRoutedEventHandler<AngularUnit> ), typeof( PolarAxes ) );
//        public event DPropChangedRoutedEventHandler<AngularUnit> DisplayUnitChanged {
//            add { AddHandler( DisplayUnitChangedEvent, value ); }
//            remove { RemoveHandler( DisplayUnitChangedEvent, value ); }
//        }
//        public static void AddDisplayUnitChangedHandler( DependencyObject d, DPropChangedRoutedEventHandler<AngularUnit> handler ) {
//            if (d != null && d is UIElement)
//                ((UIElement)d).AddHandler( DisplayUnitChangedEvent, handler );
//        }
//        public static void RemoveDisplayUnitChangedHandler( DependencyObject d, DPropChangedRoutedEventHandler<AngularUnit> handler ) {
//            if (d != null && d is UIElement)
//                ((UIElement)d).RemoveHandler( DisplayUnitChangedEvent, handler );
//        }
//        #endregion

//        #region DP: RadialTextMargin
//        public static readonly DependencyProperty RadialTextMarginProperty = DependencyProperty.RegisterAttached( "RadialTextMargin", typeof( double ), typeof( PolarAxes ), new FrameworkPropertyMetadata( 5d, FrameworkPropertyMetadataOptions.None, RadialTextMarginChangedStatic ) );
//        public double RadialTextMargin {
//            get { return (double)GetValue( RadialTextMarginProperty ); }
//            set { SetValue( RadialTextMarginProperty, value ); }
//        }
//        public static double GetRadialTextMargin( DependencyObject element ) { return (double)element.GetValue( RadialTextMarginProperty ); }
//        public static void SetRadialTextMargin( DependencyObject element, double value ) { element.SetValue( RadialTextMarginProperty, value ); }
//        private static void RadialTextMarginChangedStatic( DependencyObject sender, DependencyPropertyChangedEventArgs e ) { }
//        #endregion

//        private ToolTip _toolTipContent = new ToolTip() { Content = "sender,e" };
//        public override ToolTip ToolTipContent {
//            get { return _toolTipContent; }
//        }
//        static PolarAxes() {
//            AbstractAxes.MinimumTickSpacingProperty.OverrideMetadata( typeof( PolarAxes ), new FrameworkPropertyMetadata( 60d, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsRender ) );
//        }

//        private VisibleScale _radialAxis = new VisibleScale();
//        public VisibleScale RadialAxis {
//            get { return _radialAxis; }
//            set { _radialAxis = value; }
//        }
//        protected readonly VisibleScale AxialScale = new VisibleScale() { Domain = new Limits( 0, 2 * Math.PI ), Flags = VisibleScaleFlags.LockRange, NumTicks = 9 };

//        public new EllipseGeometry PlotArea {
//            get { return (EllipseGeometry)base.PlotArea; }
//            set { base.PlotArea = value; }
//        }

//        public PolarAxes() {
//            PlotArea = new EllipseGeometry();
//            RadialAxis.Range = new Limits( 0, 1 );
//            AxialScale.Range = new Limits( 0, DisplayUnit.FullCircle );
//            AxialScale.PrepTicks();
//            DisplayUnitChanged += OnDisplayUnitChanged;
//        }

//        protected override void OnAddPlot( Plot basedOn ) {
//            base.OnAddPlot( basedOn );
//            //Note: DataSource does not include transform!
//            basedOn.DataSource.DataChanged += OnPlotSourceDataChanged;
//            Limits buffer = Limits.Union( RadialAxis.Range, basedOn.DataSource.Limits[1] );//Take just the radius limits
//            buffer.Max = Math.Max( buffer.Max, -buffer.Min );
//            buffer.Min = 0;
//            RadialAxis.Range = buffer;
//            //RadialAxis.ShowAll();
//        }

//        private Insets scaleInsets = new Insets();
//        private void generateTicks( double radius ) {
//            scaleInsets = new Insets( 4 * ScaleTextSize, ScaleTextSize );
//        }

//        protected override void SetPlotArea( Rect graphSize ) {
//            double radius = Math.Min( graphSize.Height, graphSize.Width ) / 2;
//            generateTicks( radius );
//            graphSize -= scaleInsets;
//            PlotArea.Center = new Point( scaleInsets.Left + graphSize.Width / 2, scaleInsets.Top + graphSize.Height / 2 );
//            RadialAxis.Domain = new Limits( 0, radius = PlotArea.RadiusX = PlotArea.RadiusY = Math.Min( graphSize.Height, graphSize.Width ) / 2 ); //This will have changed since caluclating radius
//        }

//        protected override Rect GetPlotLayout( Plot basedOn ) {
//            ISeries series = (ISeries)basedOn.PlotData;
//            Point Origin = PlotArea.Center;
//            double lt = Origin.X + RadialAxis.ToPlotSpace( series.Limits[0].Min );
//            double tp = Origin.Y + RadialAxis.ToPlotSpace( series.Limits[1].Min );
//            double rt = Origin.X + RadialAxis.ToPlotSpace( series.Limits[0].Max );
//            double bm = Origin.Y + RadialAxis.ToPlotSpace( series.Limits[1].Max );
//            return new Rect( lt, tp, Math.Abs( rt - lt ), Math.Abs( bm - tp ) );
//        }

//        protected virtual void OnDisplayUnitChanged( object sender, DPropChangedRoutedEventArgs<AngularUnit> e ) {
//            AxialScale.Range = new Limits( 0, e.NewValue.FullCircle );
//            if (e.NewValue == AngularUnit.Degree || e.NewValue == AngularUnit.Clock) {
//                AxialScale.NumTicks = 12;
//                AxialScale.Flags |= VisibleScaleFlags.LockNumTicks;
//            } else
//                AxialScale.Flags &= ~VisibleScaleFlags.LockNumTicks;
//            AxialScale.PrepTicks();
//        }

//        protected override Common.ITransformedSeries GetPlotDataTransform( Plot obj ) {
//            AngularUnit input = (AngularUnit)obj.GetValue( DefaultUnitProperty );
//            PolarTransformedSeries pts = new PolarTransformedSeries( input == null ? DefaultUnit : input );
//            return pts;
//        }

//        protected override void DrawBackground( DrawingContext cx ) {
//            cx.DrawGeometry( PlotBackground, ScalePen, PlotArea );
//            Point Origin = PlotArea.Center;
//            double radius = PlotArea.RadiusX;
//            double minRat = 5;
//            double plotVal;
//            Circ contour = new Circ( Origin, 0 );
//            //Draw contours of constant radius
//            for (int i = 0; i < minRat * RadialAxis.NumTicks; i++) {
//                decimal val = RadialAxis.GetTick( i / minRat, out plotVal );
//                contour.Radius = plotVal;
//                if (i % minRat != 0) {
//                    cx.DrawEllipse( null, MinorGridlines, contour );
//                    continue;
//                }
//                cx.DrawEllipse( null, MajorGridlines, contour );
//            }
//            //Draw and Label lines of constant angle
//            for (int i = 1; i < minRat * AxialScale.NumTicks; i++) {
//                decimal val = AxialScale.GetTick( i / minRat, out plotVal );
//                contour.Radius = plotVal;
//                double sender = Origin.X + radius * Math.Cos( plotVal );
//                double e = Origin.Y - radius * Math.Sin( plotVal );
//                if (i % minRat != 0) {
//                    cx.DrawLine( MinorGridlines, Origin, new Point( sender, e ) );
//                    continue;
//                }
//                cx.DrawLine( MajorGridlines, Origin, new Point( sender, e ) );
//                Size sz = new Size();
//                GlyphRun txt = CreateGlyphRun( DisplayUnit.ToString( val ), out sz );
//                double dx = (sz.Width / 2) * (Math.Cos( plotVal ) - 1) + RadialTextMargin * Math.Cos( plotVal );
//                double dy = (sz.Height / 2) * (1 - Math.Sin( plotVal )) - RadialTextMargin * Math.Sin( plotVal );
//                cx.PushTransform( new TranslateTransform( sender + dx, e + dy ) );
//                cx.DrawGlyphRun( ScaleText, txt );
//                cx.Pop();
//            }
//            //Label Contours of constant radius
//            for (int i = 0; i <= RadialAxis.NumTicks; i++) {
//                decimal val = RadialAxis.GetTick( i, out plotVal );
//                Size sz = new Size();
//                GlyphRun txt = CreateGlyphRun( val.ToString(), out sz );
//                cx.PushTransform( new TranslateTransform( Origin.X + plotVal - sz.Width / 2, Origin.Y + sz.Height ) );
//                cx.DrawGlyphRun( ScaleText, txt );
//                cx.Pop();
//            }
//            cx.DrawLine( ScalePen, Origin, new Point( Origin.X + radius, Origin.Y ) );
//        }

//        protected void OnPlotSourceDataChanged( object sender, EventArgs e ) {
//            Plot basedOn = sender as Plot;
//            Limits buffer = Limits.Union( RadialAxis.Range, basedOn.DataSource.Limits[1] );//Take just the radius limits
//            buffer.Max = Math.Max( buffer.Max, -buffer.Min );
//            buffer.Min = 0;
//            RadialAxis.Range = buffer;
//        }

//        protected override void OnRenderSizeChanged( SizeChangedInfo sizeInfo ) {
//            base.OnRenderSizeChanged( sizeInfo );
//            RadialAxis.NumTicks = (int)(Math.Min( sizeInfo.NewSize.Width, sizeInfo.NewSize.Height ) / MinimumTickSpacing);
//            /*VisibleScale.TickDescription ticks = RadialAxis.CalculateTicks();
//            if (ticks.nTotal != RadialAxis.Ticks.Count) {
//                RadialAxis.VisibleDomain = new Limits( 0, (double)(ticks.firstTick + ticks.nTotal * ticks.tickInterval) );
//                RadialAxis.Ticks.Clear();
//                decimal val = ticks.firstTick;
//                for (int i = 0; i < ticks.nTotal; i++) {
//                    Size szText = new Size();
//                    GlyphRun gr = ticks.IsMajor( i ) ? CreateGlyphRun( val.ToString(), out szText ) : null;
//                    RadialAxis.Ticks.Add( new TickTextDescriptor( val, gr, szText.Width, szText.Height ) );
//                    val += ticks.tickInterval;
//                }
//            }
//            InvalidateVisual();*/
//        }

//        protected override void OnAxisSetChanged( Plot basedOn, int oldAxisSet, int newAxisSet ) {
//            throw new NotImplementedException();
//        }

//        protected override Visual SelectionShape {
//            get { throw new NotImplementedException(); }
//        }

//        protected override Vector ToDataSpace( Point coord ) {
//            return new Vector(
//                RadialAxis.ToDataSpace( coord.X ) - PlotArea.Center.X,
//                RadialAxis.ToDataSpace( coord.Y ) - PlotArea.Center.Y
//                );
//        }
//        protected override Point ToPlotSpace( Vector datum ) {
//            return new Point(
//                PlotArea.Center.X + RadialAxis.ToPlotSpace( datum[0] ),
//                PlotArea.Center.Y + RadialAxis.ToPlotSpace( datum[1] )
//                );
//        }
//    }


public class PolarTransformedSeries : IterativeSeriesTransform {
	private delegate double DoubleConversion( double x );
	private DoubleConversion ToRadians;

	public PolarTransformedSeries() {
		ToRadians = ( x ) => x;
	}
	public PolarTransformedSeries( AngularUnit unit ) {
		ToRadians = ( x ) => x * AngularUnit.Radian.FullCircle / unit.FullCircle;
	}

	public override Vector Transform( Vector source, object context ) {
		double th = ToRadians( source[0] );
		return new Vector( source[1] * Math.Cos( th ), source[1] * Math.Sin( th ) );
	}
}
//}
